package net.sunofbeach.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import net.sunofbeach.blog.mapper.ArticleMapper;
import net.sunofbeach.blog.mapper.LabelMapper;
import net.sunofbeach.blog.pojo.Article;
import net.sunofbeach.blog.pojo.Label;
import net.sunofbeach.blog.pojo.User;
import net.sunofbeach.blog.pojo.query.ArticleQuery;
import net.sunofbeach.blog.pojo.vo.ArticleVo;
import net.sunofbeach.blog.response.Result;
import net.sunofbeach.blog.response.ResultEnum;
import net.sunofbeach.blog.service.ArticleService;
import net.sunofbeach.blog.service.UserService;
import net.sunofbeach.blog.utils.Constants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Bing
 * @since 2021-11-24
 */
@Slf4j
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {


    /**
     * 条件分页查询文章
     *
     * @param pageParam
     * @param query
     * @return
     */
    @Override
    public IPage<Article> selectPage(Page<Article> pageParam, ArticleQuery query) {

        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();

        if (!StringUtils.isEmpty(query.getId())) {
            wrapper.eq(Article::getId, query.getId());
        }
        if (!StringUtils.isEmpty(query.getTitle())) {
            wrapper.like(Article::getTitle, query.getTitle());
        }
        if (!StringUtils.isEmpty(query.getUsername())) {
            wrapper.like(Article::getUsername, query.getUsername());
        }
        if (!StringUtils.isEmpty(query.getCategoryId())) {
            wrapper.eq(Article::getCategoryId, query.getCategoryId());
        }
        if (!StringUtils.isEmpty(query.getLabel())) {
            wrapper.like(Article::getLabel, query.getLabel());
        }
        if (!StringUtils.isEmpty(query.getState())) {
            wrapper.eq(Article::getState, query.getState());
        }
        if (!StringUtils.isEmpty(query.getBegin())) {
            wrapper.ge(Article::getCreateTime, query.getBegin());
        }
        if (!StringUtils.isEmpty(query.getEnd())) {
            wrapper.le(Article::getCreateTime, query.getEnd());
        }
        wrapper.orderByDesc(Article::getCreateTime);
        User user = userService.checkSobUser();
        // 普通用户获取已发布和置项的文章
        if (user == null || !Constants.User.ROLE_ADMIN.equals(user.getRole())) {
            wrapper.eq(Article::getState, Constants.Article.STATE_PUBLISH)
                    .or()
                    .eq(Article::getState, Constants.Article.STATE_TOP);
            return baseMapper.selectPage(pageParam, wrapper);
        }
        return baseMapper.selectPage(pageParam, wrapper);
    }


    /**
     * 统一修改文章状态：
     * 可以做成： 逻辑删除、置顶、待审核、审核通过、审核不通过等
     * 状态：0表示删除、1表示发布、2表示草稿、3表示置顶
     *
     * @param articleId
     * @param stateCode
     * @return
     */
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    @Override
    public Result updateStatus(String articleId, String stateCode) {
        // 先查后改状态为0表示删除
        Article article = baseMapper.selectById(articleId);
        if (article == null) {
            return Result.fail().message("该文章不存在");
        }
        // 逻辑删除：将状态改为0
        if (Constants.Article.STATE_DELETE.equals(stateCode)) {
            article.setState(stateCode);
            int result = baseMapper.updateById(article);
            return result > 0 ? Result.ok().message("删除文章成功") : Result.fail().message("删除文章失败");
        }
        // 置顶：将发布状态值1改为置顶3
        if (Constants.Article.STATE_PUBLISH.equals(article.getState())) {
            article.setState(Constants.Article.STATE_TOP);
            int result = baseMapper.updateById(article);
            return result > 0 ? Result.ok().message("文章置顶成功") : Result.fail().message("文章置顶失败");
        }
        // 取消置顶：将置顶状态值3改为发布1
        if (Constants.Article.STATE_TOP.equals(article.getState())) {
            article.setState(Constants.Article.STATE_PUBLISH);
            int result = baseMapper.updateById(article);
            return result > 0 ? Result.ok().message("取消置顶成功") : Result.fail().message("取消置顶失败");
        }
        // 如果查出来的状态为待审核，走这里，状态值为传来的，审核通过或审核不通过，看前端调用的是哪个接口
        if (Constants.Article.STATE_WAIT.equals(article.getState())) {
            article.setState(stateCode);
            baseMapper.updateById(article);
            // 返回结果看前端调用的是哪个接口，由前端给出对应的返回信息
            return Result.ok();
        }
        return Result.ok();
    }


    @Resource
    private UserService userService;

    /**
     * 根据ID获取文章详情
     *
     * @param articleId
     * @return
     */
    @Override
    public Result getByArticleId(String articleId) {
        // 查询文章
        Article article = baseMapper.selectById(articleId);
        if (article == null) {
            return Result.fail().message("该文章不存在");
        }
        // 普通用户
        User user = userService.checkSobUser();
        if (null == user || !Constants.User.ROLE_ADMIN.equals(user.getRole())) {
            // 无权访问删除的文章
            if (Constants.Article.STATE_DELETE.equals(article.getState())) {
                return Result.fail().message("无权访问，请联系管理员");
            }
            // 可以文章发布置顶的文章
            if ((Constants.Article.STATE_PUBLISH.equals(article.getState()) || Constants.Article.STATE_TOP.equals(article.getState()))) {
                return Result.ok(article);
            }
            // 无权访问草稿
            if ((Constants.Article.STATE_DRAFT.equals(article.getState()))) {
                return Result.fail().message("无权访问，请联系管理员");
            }
        }
        // 管理员可以访问所有类型的文章
        return Result.ok(article).message("获取文章成功");
    }

    @Resource
    private Random random;

    /**
     * 获取推荐文章，通过标签来计算
     *
     * @param articleId
     * @param size
     * @return
     */
    @Override
    public Result listRecommendArticle(String articleId, int size) {
        // 查询文章，不需要文章，只需要标签
        Article article = baseMapper.selectById(articleId);
        log.info("article ==> " + article);

        String labels = article.getLabel();
        // 打散标签
        List<String> labelList = new ArrayList<>();
        if (!labels.contains("-")) {
            labelList.add(labels);
        } else {
            labelList.addAll(Arrays.asList(labels.split("-")));
        }
        // 从列表中随机获取一标签，查询与此标签相似的文章
        String targetLabel = labelList.get(random.nextInt(labelList.size()));
        log.info("targetLabel ==> " + targetLabel);
        // 自定义sql语句。根据标签，查询相关文章
        List<ArticleVo> likeResultList = baseMapper.listArticleByLikeLabel("%" + targetLabel + "%", articleId, size);

        // 判断它的长度
        if (likeResultList.size() < size) {
            // 说明数量不够，获取最新的文章作为补充
            int dxSize = size - likeResultList.size();
            // 自定义sql，查询最新文章
            // 这个写法有一定的弊端，会把可能前面找到的也加进来，概率比较小，如果文章比较多
            List<ArticleVo> dxList = baseMapper.listLastedArticleBySize(articleId, dxSize);
            likeResultList.addAll(dxList);
        }
        return Result.ok(likeResultList).message("获取推荐文章成功");
    }


    /**
     * 管理员新增文章
     *
     * @param article
     * @return
     */
    @Override
    public Result addArticle(Article article) {
        // 检验当前操作用户的身份
        User currentUser = userService.checkSobUser();
        if (currentUser == null) {
            // 账号未登录
            return Result.build(null, ResultEnum.ACCOUNT_NOT_LOGIN);
        }
        // 判断角色，拥有 role_admin 角色可以操作
        if (!Constants.User.ROLE_ADMIN.equals(currentUser.getRole())) {
            // 权限不足
            return Result.build(null, ResultEnum.PERMISSION_DENIED);
        }

        // 2种可能（草稿和发布，根据状态判断，如果两者都不是，不支持此操作）
        String state = article.getState();
        if (!Constants.Article.STATE_PUBLISH.equals(state) && !Constants.Article.STATE_DRAFT.equals(state)) {
            return Result.fail().message("不支持此操作");
        }

        // 如果是发布，检查数据，草稿不需要检查
        if (Constants.Article.STATE_PUBLISH.equals(state)) {

            // title、分类ID、内容、类型、摘要、标签
            if (StringUtils.isEmpty(article.getTitle())) {
                return Result.fail().message("标题不可以为空");
            }
            if (article.getTitle().length() > Constants.Article.TITLE_MAX_LENGTH) {
                return Result.fail().message("文章标题不可以超过" + Constants.Article.TITLE_MAX_LENGTH + "个字符");
            }
            if (StringUtils.isEmpty(article.getContent())) {
                return Result.fail().message("内容不可以为空");
            }
            if (StringUtils.isEmpty(article.getSummary())) {
                return Result.fail().message("摘要不可以为空");
            }
            if (article.getSummary().length() > Constants.Article.SUMMARY_MAX_LENGTH) {
                return Result.fail().message("摘要不可以超出" + Constants.Article.SUMMARY_MAX_LENGTH + "个字符");
            }
            if (StringUtils.isEmpty(article.getLabel())) {
                return Result.fail().message("标签不可以为空");
            }
        }

        String articleId = article.getId();

        // 修改草稿（有或没ID，状态为草稿的文章）
        // 推荐做法：
        // 自动保存草稿，在前端本地完成，也就是保存在本地
        // 如果是用户手动提交的，就提交到后台，问题是如果修改已提交有ID的草稿

        if (StringUtils.isEmpty(articleId)) {
            // 没有ID，新增的文章
            // 补充数据
            article.setUserId(currentUser.getId());
            article.setUsername(currentUser.getUsername());
            article.setUserAvatar(currentUser.getAvatar());
            // 时间为自动填充
            // 新增草稿
            if (article.getState().equals(Constants.Article.STATE_DRAFT)) {
                // 时间为自动填充
                // 调用本类方法，打散标签，入库，统计
                if (article.getLabel() != null) {
                    this.setupLabel(article.getLabel());
                }
                int result = baseMapper.insert(article);
                return result > 0 ? Result.ok().message("草稿保存成功") : Result.fail().message("草稿保存失败");
            }
            // 新增发布文章
            // 调用本类方法，打散标签，入库，统计
            this.setupLabel(article.getLabel());
            int result = baseMapper.insert(article);
            return result > 0 ? Result.ok().message("文章发表成功") : Result.fail().message("文章发表失败");
        } else {
            // 有ID为更新内容
            // 对状态进行处理，如果已经发布，不能再保存为草稿
            Article articleFromDb = baseMapper.selectById(articleId);
            if (Constants.Article.STATE_PUBLISH.equals(articleFromDb.getState()) && Constants.Article.STATE_DRAFT.equals(state)) {
                return Result.fail().message("已发布文章不支持保存为草稿");
            }
            int result = baseMapper.updateById(article);
            // TODO 保存到搜索的数据库里
            // 返回结果，只有一种 case 使用到这个 ID
            // 如果要做程序自动保存成草稿（比如说每30秒保存一次，就需要加上这个ID，否则会创建多个Item）
            return result > 0 ? Result.ok().message("文章发表成功") : Result.fail().message("文章表发失败");
        }
    }


    @Resource
    private LabelMapper labelMapper;

    /**
     * 本类内被调用的方法
     * 打散标签，入库，统计
     *
     * @param labels
     */
    private void setupLabel(String labels) {
        List<String> labelList = new ArrayList<>();
        // 打散标签
        if (labels.contains("-")) {
            labelList.addAll(Arrays.asList(labels.split("-")));
        } else {
            labelList.add(labels);
        }
        // 入库，统计
        for (String label : labelList) {
            // 找出来
            // 根据标签名更新标签的数量
            int result = labelMapper.updateLabelCountByName(label);
            if (result == 0) {
                Label targetLabel = new Label();
                targetLabel.setCount(1);
                targetLabel.setName(label);
                labelMapper.insert(targetLabel);
            }
        }
    }


}






















